#pragma once
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <sys/stat.h>

#include "../log.h"
#include "http_mime_status.hpp"

namespace http_util{
    class Util{
    public:
        static size_t Split(const std::string &src, const std::string &sep, std::vector<std::string> *out){

            size_t offset = 0;
            size_t sz = src.size();
            while(offset < sz){
                size_t pos = src.find(sep, offset);
                if(pos == std::string::npos){
                    out->push_back(src.substr(offset));
                    return out->size();
                }
                if(pos == offset) {
                    offset += sep.size();
                    continue;
                }
                out->push_back(src.substr(offset, pos - offset));
                offset = pos + sep.size();
            }
            return out->size();
        }
        static bool ReadFile(const std::string &path, std::string *buf){
            std::ifstream ifs(path, std::ios::binary);
            if(!ifs.is_open()){
                ERROR_LOG("Open %s File Failed", path.c_str());
                // ifs.close();
                return false;
            }

            size_t fsize = 0;
            ifs.seekg(0, ifs.end);
            fsize = ifs.tellg();
            ifs.seekg(0);
            buf->resize(fsize);
            ifs.read(&(*buf)[0], fsize);
            if(ifs.good() == false){
                ERROR_LOG("Read %s File Failed", path.c_str());
                // ifs.close();
                return false;
            }
            // ifs.close();
            return true;
        }
        static bool WriteFile(const std::string &path, const std::string &buf){
            std::ofstream ofs(path, std::ios::binary | std::ios::trunc);
            if(!ofs.is_open()){
                ERROR_LOG("Open File %s For Write Failed", path.c_str());
                return false;
            }
            ofs.write(buf.c_str(), buf.size());
            if(!ofs.good()){
                ERROR_LOG("Write File %s Failed", path.c_str());
                return false;
            }
            return true;
        }
        static std::string UrlEncode(const std::string &url, bool convert_space2plus = false){
            std::string ret;
            for(const char ch : url){
                if(std::isalnum(ch) || ch == '.' || ch == '-' || ch == '_' || ch == '~'){
                    ret += ch; continue;
                }
                if(convert_space2plus == true){ //CPU分支预测优化
                    if(ch == ' ') { ret += '+'; continue; }
                }
                char tmp[4] = {'\0'};
                snprintf(tmp, 4, "%%%02X", ch);
                ret += tmp;
            }
            return ret;
        }
        static char HexCharToInt(char c){
            if(c >= '0' && c <= '9') return c - '0';
            else if(c >= 'a' && c <= 'z') return c - 'a' + 10;
            else if(c >= 'A' && c <= 'Z') return c - 'A' + 10;
            return -1;
        }
        static std::string UrlDecode(const std::string &url, bool convert_plus2space){
            std::string ret;
            int sz = url.size();
            for(int i = 0; i < sz; ++i){
                if(url[i] == '%' && i + 2 < sz){
                    char v1 = HexCharToInt(url[++i]);
                    char v2 = HexCharToInt(url[++i]);
                    char v = (v1 << 4) + v2;
                    ret.push_back(v);
                    continue;
                }
                if(convert_plus2space == true){
                    if(url[i] == '+') {
                        ret.push_back(' ');
                        continue;
                    }
                }
                ret += url[i];
            }
            return ret;
        }
        static bool IsDir(const std::string &path){
            struct stat filestat;
            int ret = stat(path.c_str(), &filestat);
            if(ret <= 0){
                return false;
            }
            return S_ISDIR(filestat.st_mode);
        }
        static bool IsREG(const std::string &path){
            struct stat filestat;
            int ret = stat(path.c_str(), &filestat);
            if(ret <= 0){
                return false;
            }
            return S_ISREG(filestat.st_mode);
        }
        static std::string GetMimetype(const std::string &filename){
            std::string surfix = filename.substr(filename.find_last_of("."));
            if(surfix.empty()) {
                ERROR_LOG("Surfix Empty!");
                return "";
            }
            const auto &it = mime_stat::mime_types.find(surfix);
            if(it == mime_stat::mime_types.end()){
                return "application/octet-stream";//二进制流
            }
            return it->second;
        }
        static std::string GetStatuDesc(const int code){
            const auto &it = mime_stat::status_desc.find(code);
            if(it == mime_stat::status_desc.end()){
                return "Unknow";
            }
            return it->second;
        }
        static bool ValidPath(const std::string &path){
            std::vector<std::string> ret;
            Split(path, "/", &ret);
            int level = 0;
            for(auto &str : ret){
                if(str == "..") {
                    if(--level < 0) return false;
                }
                else{
                    ++level;
                }
            }
            return true;
        }
    };
}